#include "system.hpp"
#include <fstream>
#include <iostream>
#include <algorithm>
#include <filesystem>  // 必须包含此头文件

System::System(Threadpool<void> &threadPool) : threadPool(threadPool) {
	// 添加try catch 捕获异常
	try {
		loadData();
	} catch(const std::exception& e) {
		std::cerr << "【致命错误】 系统初始化失败!!" << e.what() << '\n';
		throw;		
	}
}

System::~System() {
	try {
        saveData(); // 确保数据持久化
    } catch (...) {
        std::cerr << "[警告] 数据保存失败" << std::endl;
    }
}

// 数据加载：带文件校验和异常处理
void System::loadData() {
	// std::ifstream inFile("../data/card_data.txt");
	// 文件路径 要保证正常 加异常
	namespace fs = std::filesystem;
	const std::string path = "../data/card_data.txt";
	if (!std::filesystem::exists(path)) {
		throw std::runtime_error("数据文件不存在");
	}
	std::ifstream inFile(path);
	if (!inFile) {
		throw std::runtime_error("无法打开数据文件: " + path + " 请检查文件权限");
	}
	// 写锁
	std::unique_lock lock(cardMutex);
	cards.clear();	// 原本的旧数据做一下清空
       
	std::string name, cardNum;
	double balance;
	bool lost;
	size_t lineNum = 0;

	// 行号再做一个异常处理
	try {
		while (inFile >> cardNum >> name >> balance >> lost) {
			++lineNum;
			cards.emplace_back(cardNum, name, balance);
			if (lost) {
				cards.back().reportLost();
			}
		}
	} catch(const std::exception& e) {
		throw std::runtime_error("数据文件格式错误（第" + std::to_string(lineNum) + "行）: " + e.what());
	}	
}

// 数据持久化：原子写入机制
void System::saveData() noexcept {
	// std::ofstream outFile("../data/card_data.txt");
	const std::string tmpPath = "../data/card_data.tmp";
	try {
		std::ofstream outFile(tmpPath);
		{
			// 读锁
			std::shared_lock lock(cardMutex);
			for (const auto& card : cards) {
				outFile << card.getCardNum() << " " << card.getName() << " " << card.getBalance() << " " << card.isLost() << "\n";
			}
		}
		// 原子文件做一下替换操作
		if (std::rename(tmpPath.c_str(), "../data/card_data.txt") != 0)		throw std::runtime_error("文件重命名失败");
	} catch(...) {
		std::remove(tmpPath.c_str()); // 清理临时文件
		std::cerr << "【严重警告】数据保存失败，临时文件已清理" << "\n";
	}
}

// 添加饭卡也要保证线程安全
// 该函数用于向系统中添加一张新的饭卡，同时保证线程安全
void System::addCard(const Card &card) {
    // 写锁
    // 使用 std::unique_lock 对互斥锁 cardMutex 进行加锁操作
    // 这样可以确保在该函数执行期间，其他线程无法访问 cards 容器，保证线程安全
    std::unique_lock lock(cardMutex);

    // 使用迭代器遍历 如果已经存在就抛出异常
    // std::find_if 是标准库中的算法，用于在指定范围内查找满足特定条件的元素
    // 这里使用 lambda 表达式作为查找条件，检查卡片的卡号是否与要添加的卡片卡号相同
    if (auto exist = std::find_if(cards.begin(), cards.end(), [&](const Card& c) {
        // 比较当前遍历到的卡片的卡号与要添加的卡片的卡号
        return c.getCardNum() == card.getCardNum();
    });
    // 如果找到了卡号相同的卡片，即迭代器 exist 不等于 cards.end()
    exist != cards.end()) {
        // 抛出一个 std::invalid_argument 异常，提示卡号已存在
        throw std::invalid_argument("添加失败：卡号 " + card.getCardNum() + " 已存在");
    }

    // 如果没有找到相同卡号的卡片，则将新卡片添加到 cards 容器中
    // cards.push_back(card);

    // 带异常的保存数据
    // 调用 saveData 函数将更新后的卡片数据保存到文件或其他存储介质中
    // 该函数可能会抛出异常，需要在调用处进行异常处理
	try {
		// cards.push_back(card);
		// cards.emplace_back(std::move(card));  // 使用移动语义
		cards.emplace_back(card.getCardNum(), card.getName(), card.getBalance());
		saveData();
	} catch(const std::exception& e) {
		cards.pop_back(); // 回滚操作保证强异常安全[1](@ref)
        throw;
	}
}

// 共享锁
Card* System::findCard(const std::string &cardNum) {
	// 读锁
    // 使用 std::shared_lock 对互斥锁 cardsMutex 进行加锁操作
    // 共享锁允许多个线程同时进行读操作，提高并发性能
	std::shared_lock lock(cardMutex);	
	for (auto& card : cards) {
		if (card.getCardNum() == cardNum) {
			// 如果找到匹配的卡片，返回该卡片的指针
			return &card;
		}
	}
	// 没有就指向空
	return nullptr;
}

// 双重校验 异步处理
void System::consumeCard(const std::string& cardNum, double amount) {
	// ========== 前置校验层 ========== 
	if (amount <= 0) {
		throw std::invalid_argument("消费金额必须大于0，当前值: " + std::to_string(amount));
	}

	// 第一重校验：卡片存在性检查（共享读锁）
	if (Card* card = findCard(cardNum)) {
		// 第二重校验：卡片状态检查（需原子操作）
		if (card->isLost()) {
			throw std::logic_error("卡片 " + cardNum + " 已挂失，禁止消费");
		}
		// ========== 异步处理层 ========== 
		threadPool.enqueueTask([this, cardNum, amount]() { // 传值代替指针避免悬垂引用
			try {
				// ========== 补偿校验层 ========== 
				std::shared_lock lock(cardMutex);
				if (Card* currentCard = findCard(cardNum)) {
					if (currentCard->isLost()) {
						throw std::logic_error("异步处理时卡片已挂失");
					}
					// 原子化操作（卡片级锁）
					std::lock_guard cardLock(currentCard->getMutex());
					currentCard->consume(amount);
					
					// 事务型数据持久化 
					saveData(); 
				} else {
					throw std::runtime_error("卡片已被删除");
				}
			} catch (const std::exception& e) {
				// ========== 异常处理层 ========== 
				std::cerr << "[异步错误] 卡号:" << cardNum 
						  << " 金额:" << amount 
						  << " 错误详情:" << e.what() << '\n';
			}
		});
	} else {
		throw std::out_of_range("卡号 " + cardNum + " 不存在");
	}
}

// 优化列表
/**
 * @brief 为指定饭卡充值（带异常处理）
 * @param cardNum 卡号
 * @param amount 充值金额（必须大于0）
 * @throw std::invalid_argument 金额非法
 * @throw std::runtime_error 数据保存失败
 * @note 线程安全：持有卡片级锁和系统级共享锁
 */
void System::rechargeCard(const std::string &cardNum, double amount) {
	if (amount <= 0)	throw std::invalid_argument("充值金额必须大于0，当前值: " + std::to_string(amount));

	// 饭卡存在检查
	if (Card* card = findCard(cardNum)) {
		std::shared_lock sharedLock(cardMutex); // 共享读锁
		if (card->isLost()) 	throw std::logic_error("卡片 " + cardNum + " 已挂失，禁止充值");
		// 异步处理确保主线程响应速度
		// ========== 异步处理层 ========== 
		threadPool.enqueueTask([this, cardNum, amount]() { // 传值代替指针避免悬垂引用
			try {
				// ========== 补偿校验层 ========== 
				std::shared_lock lock(cardMutex);		// 升级为写锁
				if (Card* currentCard = findCard(cardNum)) {
					if (currentCard->isLost()) {
						throw std::logic_error("异步处理时卡片已挂失");
					}
					// 原子化操作（卡片级锁）
					std::lock_guard cardLock(currentCard->getMutex());
					currentCard->recharge(amount);
					
					// 事务型数据持久化 
					saveData(); 
				} else {
					throw std::runtime_error("充值失败：卡片已被删除");
				}
			} catch (const std::exception& e) {
				// ========== 异常处理层 ========== 
				std::cerr << "[异步错误] 卡号:" << cardNum 
						  << " 金额:" << amount 
						  << " 错误详情:" << e.what() << '\n';
			}
		});
	} else {
		throw std::out_of_range("卡号 " + cardNum + " 不存在");
	}
}

/**
 * @brief 挂失指定饭卡
 * @param cardNum 卡号
 * @throw std::logic_error 重复挂失
 * @note 立即持久化数据，保证状态一致性
 */
void System::reportLostCard(const std::string &cardNum) {
	std::unique_lock lock(cardMutex); // 写锁
	if (Card* card = findCard(cardNum)) {
		if (card->isLost())		throw std::logic_error("卡片 " + cardNum + " 已处于挂失状态");

		card->reportLost();
		// 做一下异常处理
		try {
			saveData();		// 保存数据 持久化数据状态变更
		} catch (...) {
			// 回滚一下
			card->cancelLost();
			throw;		// 重新抛出
		}
	} else {
        throw std::out_of_range("卡号 " + cardNum + " 不存在");
    }
}

/**
 * @brief 删除指定饭卡
 * @param cardNum 卡号
 * @throw std::out_of_range 卡片不存在
 * @note 操作不可逆，需二次确认（前端实现）
 */           
void System::deleteCard(const std::string &cardNum) {
	std::unique_lock lock(cardMutex); // 写锁
	cards.erase(std::remove_if(cards.begin(), cards.end(), [&cardNum](const Card &card){
		return card.getCardNum() == cardNum;
	}), cards.end());
	saveData();
}

/**
 * @brief 列出所有饭卡信息
 * @note 使用共享锁保证遍历安全
 */
void System::listAllCards() {
	std::shared_lock lock(cardMutex); // 共享读锁
	if (cards.empty()) {
		std::cout << "当前无注册饭卡" << std::endl;
        return;
	}

	// 表格格式化输出
	// 表格化输出
    std::cout << std::left << std::setw(20) << "卡号" << std::setw(15) << "姓名" << std::setw(10) << "余额"  << "状态" << std::endl;
   
	// for(const auto& card : cards) {
	// 	std::cout << "卡号: " << card.getCardNum() << ", 姓名: " << card.getName() << ", 余额: " << card.getBalance() << ", 是否挂失: " << (card.isLost() ? "是" : "否") << std::endl;
	// }
	for(const auto& card : cards) {
		std::cout << std::setw(20) << card.getCardNum() << std::setw(15) << card.getName() << std::setw(10) << std::fixed << std::setprecision(2) << card.getBalance() << ", 是否挂失: " << (card.isLost() ? "是" : "否") << std::endl;
	}
}

/**
 * @brief 系统主运行循环
 * @note 包含输入验证和异常恢复机制
 *
 * =====================================================
 * **
 * @brief 系统主运行函数，提供用户交互菜单并处理各种操作。
 * 
 * 该函数通过一个无限循环实现用户交互，用户可以选择不同的功能选项。
 * 每个选项对应一个具体的操作，例如添加卡片、消费、充值、挂失、删除卡片或列出所有卡片。
 * 输入错误或操作失败时会捕获异常并提示用户。
 * 
 * 功能选项说明：
 * - add: 添加新卡片，要求输入卡号、姓名和初始余额。
 * - consume: 消费操作，要求输入卡号和消费金额。
 * - recharge: 充值操作，要求输入卡号和充值金额。
 * - reportLost: 挂失操作，要求输入卡号。
 * - delete_card: 删除卡片，要求输入卡号。
 * - find_list: 列出所有卡片信息。
 * 
 * 输入验证：
 * - 对用户输入进行类型和格式验证，防止非法输入导致程序崩溃。
 * - 如果输入错误，会抛出异常并提示用户重新输入。
 * 
 * 异常处理：
 * - 捕获所有标准异常（std::exception），并输出错误信息。
 * - 在发生错误时，程序会暂停一段时间以便用户查看错误信息。
 * 
 * 注意事项：
 * - 输入时请确保格式正确，例如卡号为字符串，金额为数字。
 * - 该函数为阻塞式运行，需手动退出程序。
 * 
 * 示例用法：
 * - 添加卡片：输入选项 add，然后输入卡号、姓名和初始余额。
 * - 消费：输入选项 consume，然后输入卡号和消费金额。
 * - 充值：输入选项 recharge，然后输入卡号和充值金额。
 * - 挂失：输入选项 reportLost，然后输入卡号。
 * - 删除卡片：输入选项 delete_card，然后输入卡号。
 * - 列出所有卡片：输入选项 find_list。
 */
void System::run() {
	int choice;
	while (true) {
		try {
			menu();
			// 输入验证
			if (!(std::cin >> choice)) {
				std::cin.clear();
				std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
				throw std::ios_base::failure("输入类型错误");
			}

			switch (choice) {
				case add: {
					std::string cardNum, name;
					double balance;
					std::cout << "请输入卡号, 姓名和初始余额: ";
					if (!(std::cin >> cardNum >> name >> balance)) {
						throw std::invalid_argument("输入格式错误");
					}
					addCard(Card(cardNum, name, balance));
					break;
				}
				case consume: {
					std::string cardNumber;
					double amount;
					std::cout << "请输入卡号和消费金额: ";
					if (!(std::cin >> cardNumber >> amount)) {
						throw std::invalid_argument("输入格式错误");
					}
					consumeCard(cardNumber, amount);
					break;
				}
				case recharge: {
					std::string cardNumber;
					double amount;
					std::cout << "请输入卡号和充值金额: ";
					if (!(std::cin >> cardNumber >> amount)) {
						throw std::invalid_argument("输入格式错误");
					}
					rechargeCard(cardNumber, amount);
					break;
				}
				case reportLost: {
					std::string cardNumber;
					std::cout << "请输入卡号挂失: ";
					if (!(std::cin >> cardNumber)) {
						throw std::invalid_argument("输入格式错误");
					}
					reportLostCard(cardNumber);
					break;
				}
				case delete_card: {
					std::string cardNumber;
					std::cout << "请输入卡号删除: ";
					if (!(std::cin >> cardNumber)) {
						throw std::invalid_argument("输入格式错误");
					}
					deleteCard(cardNumber);
					break;
				}
				case find_list:
					listAllCards();
					break;

				default:
					std::cout << "无效选项，请重新输入" << std::endl;
			}
		} catch (const std::exception &e) {
			std::cerr << "操作失败: " << e.what() << std::endl;
			std::this_thread::sleep_for(std::chrono::seconds(1)); // 错误展示时间
		}
	}
}

// 显式实例化模板
template class Threadpool<void>;